// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// Example of a clock. This is very similar to the text-example,
// except that it shows the time :)
//
// This code is public domain
// (but note, that the led-matrix library this depends on is GPL v2)

#include "led-matrix.h"
#include "graphics.h"

#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include <vector>
#include <string>

// demure added
#include <ctime>
#include <iostream>
#include "json.hpp"     //<nlohmann/json.hpp>
using json = nlohmann::json;


using namespace rgb_matrix;

volatile bool interrupt_received = false;
static void InterruptHandler(int signo) {
    interrupt_received = true;
}

static int usage(const char *progname) {
    fprintf(stderr, "usage: %s [options]\n", progname);
    fprintf(stderr, "Reads text from stdin and displays it. "
            "Empty string: clear screen\n");
    fprintf(stderr, "Options:\n");
    fprintf(stderr,
            //"\t-f <font-file>    : Use given font.\n"
            //"\t-C <r,g,b>        : Color. Default 255,255,0\n"
            //"\t-B <r,g,b>        : Background-Color. Default 0,0,0\n"
            "\tTest              : Description\n"
            "\n"
            );
    rgb_matrix::PrintMatrixFlags(stderr);
    return 1;
}

//static bool parseColor( Color *c, const char *str) {
  //return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3;
//}

// Initialize rgb_matrix color structs
// Values here don't matter...
Color ct_time(0, 0, 200);
Color ct_week(0, 125, 125);
Color ct_date(125, 0, 125);
Color ct_utc(150, 0, 0);
Color ct_epoch(0, 150, 0);
Color ct_temp(125, 125, 125);
Color ct_humid(125, 125, 0);
Color ct_sun(150, 75, 0);

Color ct_rand(150, 150, 150);   //DEBUG


int main(int argc, char *argv[]) {
    RGBMatrix::Options matrix_options;
    rgb_matrix::RuntimeOptions runtime_opt;
    if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv,
                                            &matrix_options, &runtime_opt)) {
        return usage(argv[0]);
    }

    // Set Default LED Matrix Conf
    matrix_options.rows = 64;
    matrix_options.cols = 64;
    matrix_options.hardware_mapping = "adafruit-hat";


    //int opt;
    //while ((opt = getopt(argc, argv, "f:C:")) != -1) {
        //switch (opt) {
        //case 'f': bdf_font_file = strdup(optarg); break;
        //case 'C':
            //if (!parseColor(&color, optarg)) {
                //fprintf(stderr, "Invalid color spec: %s\n", optarg);
                //return usage(argv[0]);
            //}
            //break;
        //default:
            //return usage(argv[0]);
        //}
    //}


    /// Font Loading /// {{{
    const char *ft_16x32_file = "/led_matrix/spleen/spleen-16x32.bdf";
    rgb_matrix::Font ft_16x32;
    if (!ft_16x32.LoadFont(ft_16x32_file)) {
        fprintf(stderr, "Couldn't load font '%s'\n", ft_16x32_file);
        return 1;
    }

    const char *ft_12x24_file = "/led_matrix/spleen/spleen-12x24.bdf";
    rgb_matrix::Font ft_12x24;
    if (!ft_12x24.LoadFont(ft_12x24_file)) {
        fprintf(stderr, "Couldn't load font '%s'\n", ft_12x24_file);
        return 1;
    }

    const char *ft_8x16_file = "/led_matrix/spleen/spleen-8x16.bdf";
    rgb_matrix::Font ft_8x16;
    if (!ft_8x16.LoadFont(ft_8x16_file)) {
        fprintf(stderr, "Couldn't load font '%s'\n", ft_8x16_file);
        return 1;
    }

    const char *ft_5x8_file = "/led_matrix/spleen/spleen-5x8.bdf";
    rgb_matrix::Font ft_5x8;
    if (!ft_5x8.LoadFont(ft_5x8_file)) {
        fprintf(stderr, "Couldn't load font '%s'\n", ft_5x8_file);
        return 1;
    }
    /// End Font Loading /// }}}

    RGBMatrix *matrix = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt);
    if (matrix == NULL)
        return 1;

    FrameCanvas *offscreen = matrix->CreateFrameCanvas();

    struct timespec next_time;
    next_time.tv_sec = time(NULL);
    next_time.tv_nsec = 0;
    struct tm tm;

    signal(SIGTERM, InterruptHandler);
    signal(SIGINT, InterruptHandler);


    // Set Background Color
    Color bg_color(0, 0, 0);


    while (!interrupt_received) {
        // Clear screen by overwriting with background color
        offscreen->Fill(bg_color.r, bg_color.g, bg_color.b);
        localtime_r(&next_time.tv_sec, &tm);


        /// Set Time Vars /// {{{
        // Normal Time
        time_t rawtime;
        struct tm * timeinfo;

        time (&rawtime);
        timeinfo = localtime (&rawtime);

        // Declare Normal Time Vars
        char t_time [8];
        char t_time_late [8];
        char t_week [10];
        char t_week_late [8];
        char t_date [16];
        char t_epoch [16];
        char t_hour [8];

        // Format Normal Time
        strftime (t_time,8,"%R",timeinfo);
        strftime (t_time_late,8,"%H%M",timeinfo);
        strftime (t_week,10,"%a, %b",timeinfo);
        strftime (t_week_late,8,"%a",timeinfo);
        strftime (t_date,16,"%F",timeinfo);
        strftime (t_epoch,16,"%s",timeinfo);
        strftime (t_hour,8,"%H",timeinfo);


        // Normal Time Vars for Tests
        int now_h = atoi(t_hour);
        int now_s = atoi(t_epoch);


        // UTC Time
        time_t utc_time;
        struct tm * tm_gmt;

        utc_time = time(NULL);
        tm_gmt = gmtime(&utc_time);

        char t_utc [10];
        strftime (t_utc,10,"%R UTC",tm_gmt);
        /// End Set Time Vars /// }}}



        /// Weather ///

        // Weather Lookup
        auto weather = json::parse(R"({"coord": {"lon": -10.000, "lat": 10.0000}, "weather": [{"id": 800, "main": "Clear", "description": "clear sky", "icon": "01n"}], "base": "stations", "main": {"temp": 292.48, "feels_like": 292.22, "temp_min": 290.19, "temp_max": 295.2, "pressure": 1016, "humidity": 67}, "visibility": 10000, "wind": {"speed": 2.06, "deg": 340}, "clouds": {"all": 1}, "dt": 1625274979, "sys": {"type": 2, "id": 1000000, "country": "US", "sunrise": 1625220143, "sunset": 1625275865}, "timezone": -14400, "id": 0000000, "name": "CITY", "cod": 200})");     //DEBUG

        // Weather Vars
        // Weather Pull Status
        int wi_cod = weather["cod"].get<int>();


        // Get K temp, Math K to F, and store as char
        char wc_temp_f [8];
        std::sprintf(wc_temp_f, "%dF", (int)round((weather["main"]["temp"].get<int>() - 273.15) * 9/5 + 32));

        // Humidity
        char wc_humid [8];
        std::sprintf(wc_humid, "%d%%H", weather["main"]["humidity"].get<int>());

        // Sunrise
        // TODO: strftime and store char array %H%M version for LED display
        int wi_sunrise = weather["sys"]["sunrise"].get<int>();
        int wi_sunset = weather["sys"]["sunset"].get<int>();

        // Convert Weather Sun Times
        time_t tm_sunrise = wi_sunrise;
        char wc_sunrise [8];
        strftime (wc_sunrise,8,"%H%M*",localtime(&tm_sunrise));

        time_t tm_sunset = wi_sunset;
        char wc_sunset [8];
        strftime (wc_sunset,8,"%H%M*",localtime(&tm_sunset));

        // Store Last Weather Update Time
        int wi_up = weather["dt"].get<int>();
        /// End Weather ///


        /// Test for Night Mode /// {{{
        bool night_mode;    // declare var
        if (wi_sunset != 0 and (now_s - wi_up) < 21600) {
            if (now_s > wi_sunset or now_s < wi_sunrise) {
                night_mode = true;
            }
            else {
                night_mode = false;
            }
        }
        else {
            if (now_h < 7 or now_h > 21) {
                night_mode = true;
            }
            else {
                night_mode = false;
            }
        }
        /// End Test for Night Mode /// }}}


        /// Set Output Colors /// {{{
        // Test for night mode
        //if (now_h < 7 or now_h > 21) {
        if (night_mode == true) {
            ct_time.r  = 30;  ct_time.g  = 0;   ct_time.b  = 0;
            ct_week.r  = 30;  ct_week.g  = 0;   ct_week.b  = 0;
            ct_date.r  = 30;  ct_date.g  = 0;   ct_date.b  = 0;
            ct_utc.r   = 30;  ct_utc.g   = 0;   ct_utc.b   = 0;
            ct_epoch.r = 30;  ct_epoch.g = 0;   ct_epoch.b = 0;
            ct_temp.r  = 30;  ct_temp.g  = 0;   ct_temp.b  = 0;
            ct_humid.r = 30;  ct_humid.g = 0;   ct_humid.b = 0;
            ct_sun.r   = 30;  ct_sun.g   = 0;   ct_sun.b   = 0;

            ct_rand.r  = 30;  ct_rand.g  = 0;   ct_rand.b  = 0;     //DEBUG
        }
        // Else Day mode
        else {
            ct_time.r  = 0;   ct_time.g  = 0;   ct_time.b  = 200;
            ct_week.r  = 0;   ct_week.g  = 125; ct_week.b  = 125;
            ct_date.r  = 125; ct_date.g  = 0;   ct_date.b  = 125;
            ct_utc.r   = 150; ct_utc.g   = 0;   ct_utc.b   = 0;
            ct_epoch.r = 0;   ct_epoch.g = 150; ct_epoch.b = 0;
            ct_temp.r  = 125; ct_temp.g  = 125; ct_temp.b  = 125;
            ct_humid.r = 125; ct_humid.g = 125; ct_humid.b = 0;
            ct_sun.r   = 150; ct_sun.g   = 75;  ct_sun.b   = 0;

            //ct_rand.r  = 30;  ct_rand.g  = 0;   ct_rand.b  = 0;     //DEBUG
            ct_rand.r  = rand() % 100;  ct_rand.g  = rand() % 100;   ct_rand.b  = rand() % 100;     //DEBUG
        }
        ///End Set Output Colors /// }}}


        /// Display LED output ///
        if (now_h <= 6) {
            //                    Location, font, X, Y, Color, BG_Color, String, Char_Spacing;
            rgb_matrix::DrawText(offscreen, ft_16x32, 0,  20, ct_time,  NULL, t_time_late, 0);
            rgb_matrix::DrawText(offscreen, ft_16x32, 0,  42, ct_week,  NULL, t_week_late, 0);
            // Display Night Temp
            rgb_matrix::DrawText(offscreen, ft_16x32, 0,  64, ct_temp,  NULL, wc_temp_f, 0);
        } else {
            //                    Location, font, X, Y, Color, BG_Color, String, Char_Spacing;
            rgb_matrix::DrawText(offscreen, ft_12x24, 2,  15, ct_time,  NULL, t_time, 0);

            rgb_matrix::DrawText(offscreen, ft_5x8,   0,  43, ct_week,  NULL, t_week, 0);
            rgb_matrix::DrawText(offscreen, ft_5x8,   0,  50, ct_date,  NULL, t_date, 0);
            rgb_matrix::DrawText(offscreen, ft_5x8,   0,  57, ct_utc,   NULL, t_utc, 0);
            rgb_matrix::DrawText(offscreen, ft_5x8,   0,  64, ct_epoch, NULL, t_epoch, 0);

            // Display Day Weather
            rgb_matrix::DrawText(offscreen, ft_8x16,  0,  27, ct_temp,  NULL, wc_temp_f, 0);
            rgb_matrix::DrawText(offscreen, ft_5x8,   0,  35, ct_humid, NULL, wc_humid, 0);
            //rgb_matrix::DrawText(offscreen, ft_5x8,   25, 35, ct_sun,   NULL, "XXXX*", 0);

            if (now_s < wi_sunrise) {
                //printf("Sunrise is next at %s.\n",wc_sunrise);
                //printf("Sunrise is next at %d.\n",wi_sunrise);
                rgb_matrix::DrawText(offscreen, ft_5x8,   25, 35, ct_sun,   NULL, wc_sunrise, 0);
            }
            else if (now_s < wi_sunset) {
                //printf("Sunset is next at %s.\n",wc_sunset);
                //printf("Sunset is next at %d.\n",wi_sunset);
                rgb_matrix::DrawText(offscreen, ft_5x8,   25, 35, ct_sun,   NULL, wc_sunset, 0);
            }
        }

        rgb_matrix::DrawText(offscreen, ft_5x8,   59,  64, ct_rand,  NULL, "+", 0);      //DEBUG
        /// End Display LED output ///


        // Wait until we're ready to show it.
        clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next_time, NULL);

        // Atomic swap with double buffer
        offscreen = matrix->SwapOnVSync(offscreen);

        next_time.tv_sec += 1;
    }

    // Finished. Shut down the RGB matrix.
    delete matrix;

    write(STDOUT_FILENO, "\n", 1);  // Create a fresh new line after ^C on screen
    return 0;
}
